package edu.pucsp.predatorprey.simulador.erlang;

import java.io.IOException;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangDecodeException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangInt;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.ericsson.otp.erlang.OtpMbox;
import com.ericsson.otp.erlang.OtpNode;

import edu.pucsp.predatorprey.simulador.api.SaidaSimulacao;
import edu.pucsp.predatorprey.simulador.api.Simulador;

public class SimuladorErlang implements Simulador, Runnable {

	private static final String SIMULADOR_ERLANG = "simulador";
	private static final String SERVER = "server";

	private SaidaSimulacao saida;

	private OtpNode node;
	private OtpMbox mbox;
	private boolean executando = true;
	private final int qtdPresas;
	private final int qtdPredador;

	public SimuladorErlang(int qtdPresas, int qtdPredador) {
		this.qtdPresas = qtdPresas;
		this.qtdPredador = qtdPredador;
		try {
			this.node = new OtpNode("javaNode", "defaultCookie");
			this.mbox = this.node.createMbox("simuladorServer");
			if (!this.node.ping(SERVER, 2000)) {
				this.derrubarNode();
				throw new SimuladorErlangException("Server is not up.");
			}
		} catch (IOException e) {
			throw new SimuladorErlangException("Falha ao iniciar simulador erlang.", e);
		}
	}

	@Override
	public void iniciarSimulacao() {
		Thread t = new Thread(this);
		t.start();
		OtpErlangObject[] qtdCriaturas = new OtpErlangObject[2];
		qtdCriaturas[0] = new OtpErlangInt(this.qtdPresas);
		qtdCriaturas[1] = new OtpErlangInt(this.qtdPredador);
		OtpErlangTuple criaturas = new OtpErlangTuple(qtdCriaturas);
		OtpErlangObject[] msg = new OtpErlangObject[3];
		msg[0] = new OtpErlangAtom("iniciarSimulacao");
		msg[1] = this.mbox.self();
		msg[2] = criaturas;
		this.enviarMensagem(msg);
	}

	@Override
	public void pararSimulacao() {
		OtpErlangObject[] msg = new OtpErlangObject[1];
		msg[0] = new OtpErlangAtom("pararSimulacao");
		this.enviarMensagem(msg);
	}

	private void processarMensagem(String messageId, OtpErlangTuple msg) {
		ProcessadorMensagem.getProcessador(messageId).processarMensagem(msg, this.saida, this);
	}

	public void executando(boolean exec) {
		this.executando = exec;
	}

	@Override
	public void definirSaida(SaidaSimulacao saida) {
		this.saida = saida;
	}

	private void enviarMensagem(OtpErlangObject[] params) {
		OtpErlangTuple tuple = new OtpErlangTuple(params);
		this.mbox.send(SIMULADOR_ERLANG, SERVER, tuple);
	}

	public void derrubarNode() {
		this.node.closeMbox(this.mbox);
		this.node.close();
	}

	@Override
	public void run() {
		while (this.executando) {
			try {
				OtpErlangTuple msg = (OtpErlangTuple) this.mbox.receive();
				OtpErlangAtom atom = (OtpErlangAtom) msg.elementAt(0);
				this.processarMensagem(atom.atomValue(), msg);

			} catch (OtpErlangExit e) {
				e.printStackTrace();
			} catch (OtpErlangDecodeException e) {
				e.printStackTrace();
			}
		}
	}

}
